-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for custom scripts #1056
base: main
Are you sure you want to change the base?
Conversation
@available(*, deprecated, renamed: "init(info:baseURL:symbolGraphURLs:markupURLs:miscResourceURLs:customHeader:customFooter:themeSettings:)", message: "Use 'init(info:baseURL:symbolGraphURLs:markupURLs:miscResourceURLs:customHeader:customFooter:themeSettings:)' instead. This deprecated API will be removed after 6.1 is released") | ||
public init( | ||
info: Info, | ||
baseURL: URL = URL(string: "/")!, | ||
attributedCodeListings: [String: AttributedCodeListing] = [:], | ||
symbolGraphURLs: [URL], | ||
markupURLs: [URL], | ||
miscResourceURLs: [URL], | ||
customHeader: URL? = nil, | ||
customFooter: URL? = nil, | ||
themeSettings: URL? = nil | ||
) { | ||
self.init(info: info, baseURL: baseURL, symbolGraphURLs: symbolGraphURLs, markupURLs: markupURLs, miscResourceURLs: miscResourceURLs, customHeader: customHeader, customFooter: customFooter, themeSettings: themeSettings) | ||
self.attributedCodeListings = attributedCodeListings | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a source breaking change. Like the deprecation message says; we can't remove this until after 6.1 is released. See the "Introducing source breaking changes" section of the contributions guidelines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @d-ronnqvist,
Thank you for the comments! And apologies for the delayed response.
I agree that I should not remove the deprecated init in this PR: in truth, I only removed the deprecated init in this first revision to ask for help in the PR discussion for how to best avoid making this breaking change. I’m unable to build swift-docc when both versions of the init exist; this happens only with my changes, and I’m not sure why.
To explain the issue I’m facing, consider as an example the following call to DocumentationBundle.init
in ConvertService+DataProvider.swift
:
bundles.append(
DocumentationBundle(
info: info,
symbolGraphURLs: symbolGraphURLs,
markupURLs: markupFileURLs,
miscResourceURLs: miscResourceURLs
)
)
The only difference between the deprecated DocumentationBundle
init and the non-deprecated init is that the deprecated init has an attributedCodeListings
parameter. That parameter is optional, so in theory, the call above could be either to the non-deprecated init or to deprecated init. However, the compiler disambiguates the call by prioritizing the non-deprecated init.
But when I added customScripts
as an optional parameter to the non-deprecated init — and changed the deprecated init to pass customScripts: nil
to the non-deprecated init — I got an “Ambiguous use of 'init'” error at the call above.
But why? Surely if the compiler could disambiguate the init call before I added the customScripts
parameter (by prioritizing the non-deprecated init) then it should be able to do the same after I added customScripts
, no? Maybe the compiler’s disambiguation rules are more nuanced than I realize.
Regardless, unless there’s a better solution to this “Ambiguous use of 'init'” problem I'll just manually disambiguate the calls by explicitly specifying customScripts: nil
where appropriate. I can also add a comment clarifying that the explicit customScripts: nil
can be removed after Swift 6.1 (when the deprecated init is removed), like so:
bundles.append(
DocumentationBundle(
info: info,
symbolGraphURLs: symbolGraphURLs,
markupURLs: markupFileURLs,
miscResourceURLs: miscResourceURLs,
customScripts: nil // Explicit `customScripts: nil` can be removed after the deprecated `DocumentationBundle.init` is removed.
)
)
If this would not be the best way to circumvent the init ambiguity, please let me know. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC you can add the @_disfavoredOverload
attribute to the deprecated initializer to avoid the ambiguity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is making an unintentional source breaking change that should be undone.
Additionally, we're in the process of migrating away from LocalFileSystemDataProvider
so the new code to find the custom-scripts.json file also need to be added to DocumentationContext.InputProvider
. There's already a test that verifies that both implementation discover the same files which would also be good to update so that it verifies the behavior for this new type of file.
/// | ||
/// - Parameter bundleIdentifier: The identifier of the bundle to return download assets for. | ||
/// - Returns: A list of all the custom scripts for the given bundle. | ||
public func registeredCustomScripts(forBundleID bundleIdentifier: BundleIdentifier) -> [DataAsset] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer if this was package
level access for now so that we don't have to make more source breaking changes to change it to not have a bundle ID parameter when #1059 lands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
@@ -3251,6 +3251,7 @@ class ConvertActionTests: XCTestCase { | |||
|
|||
XCTAssertEqual(fileSystem.dump(subHierarchyFrom: targetURL.path), """ | |||
Output.doccarchive/ | |||
├─ custom-scripts/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: is it expected that we create this folder even if the user didn't pass any custom scripts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is! Every subfolder of a documentation archive is created even when they are no files to add to the subfolder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, the method of input discovery in this file is about to be deprecated in #1059 and won't be used by DocC anymore.
These same changes need to be implemented in DocumentationContext.InputProvider which is what DocC will use to discover its inputs after #1057 lands.
It would also be good to update DocumentationInputsProviderTests.testDiscoversSameFilesAsPreviousImplementation()
to verify that both implementations discover the custom scripts file the same. (This can happen before either of those PRs are merged)
self.rootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: "/", sourceLanguage: .swift) | ||
self.documentationRootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: NodeURLGenerator.Path.documentationFolder, sourceLanguage: .swift) | ||
self.tutorialsRootReference = ResolvedTopicReference(bundleIdentifier: info.identifier, path: NodeURLGenerator.Path.tutorialsFolder, sourceLanguage: .swift) | ||
self.technologyTutorialsRootReference = tutorialsRootReference.appendingPath(urlReadablePath(info.displayName)) | ||
self.articlesDocumentationRootReference = documentationRootReference.appendingPath(urlReadablePath(info.displayName)) | ||
} | ||
|
||
@available(*, deprecated, renamed: "init(info:baseURL:symbolGraphURLs:markupURLs:miscResourceURLs:customHeader:customFooter:themeSettings:)", message: "Use 'init(info:baseURL:symbolGraphURLs:markupURLs:miscResourceURLs:customHeader:customFooter:themeSettings:)' instead. This deprecated API will be removed after 6.1 is released") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FIY: Since the name of the replacement changed with the new parameter you may need to update the renamed
portion of this deprecation annotation. Even if it's not needed, it's nice to do it since it enables the fixit for any code that still uses the deprecated API.
@@ -120,6 +120,18 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer { | |||
for downloadAsset in context.registeredDownloadsAssets(forBundleID: bundleIdentifier) { | |||
try copyAsset(downloadAsset, to: downloadsDirectory) | |||
} | |||
|
|||
// Create custom scripts directory if needed. Do not append the bundle identifier. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not append the bundle ID here?
Summary
First-class support for adding custom scripts to a DocC-generated website. These may be local scripts, in which case the website will continue to work offline, or they may be external scripts at a user-specified URL. This support is in the form of a custom-scripts.json file, the scripting analog of theme-settings.json.
Full pitch: https://forums.swift.org/t/pitch-support-for-custom-scripts-in-docc/75357.
Dependencies
Corresponding change in swift-docc-render.
Testing
custom-scripts.json
and the custommathjax-config.js
script (shown in the pitch) to your documentation catalog.docc convert
the documentation catalog with your custom scripts. Observe that the modified swift-docc copiedcustom-scripts.json
and the custom scripts into the documentation archive.Checklist
Make sure you check off the following items. If they cannot be completed, provide a reason.
./bin/test
script and it succeeded